package data_creation;

import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Set;

import poki.model.Card;
import poki.model.CardSet;
import poki.model.HandRanking;

public class Simulator {
	
	public Simulator(){
		
	}

	//Gibt alle noch verf�gbaren Karten zur�ck
	public CardSet getAllowedCards(CardSet forbiddenCards){
		
		CardSet allowedCards = new CardSet();
		for(int i = 0; i < 13; i++)
			for (int j = 0; j < 4; j++) {
				allowedCards.addCard(new Card(i + 2, j + 1));
			}
		
		return allowedCards.disMerge(forbiddenCards);
	}
	
	//TODO
	public double[] calculateProbForAllEnemys(CardSet initHand, int numberOfOpponents, CardSet board){
		
		double[] probabilities = new double[3]; 	//1. Eintrag Win, 2. Eintrag Splitpot, 3. Eintrag lose
		
		//int numberOfGames = allOpponentSetComb.size();	//Hängt davon ab wieviele Gegnerische Kartenkonfigurationen vorhanen sind
		return probabilities;
	}
	
	
	public double[] calculateProbabilities(CardSet initHand, SetOfCardSets opponentHands, CardSet board){
		
		if(opponentHands == null){
			return null; 		//Keine Gegner vorhanden
		}
		if(board == null){
			board = new CardSet();
		}
		
		HandRanking handRanking = new HandRanking();
		double[] amountOfWinSplitLose = new double[3];		//1. Eintrag Win, 2. Eintrag Splitpot, 3. Eintrag lose
		double[] probabilities = new double[3]; 	//1. Eintrag Win, 2. Eintrag Splitpot, 3. Eintrag lose
		SetOfCardSets allSimulatedBoards = this.simulateAllBoards(initHand, opponentHands, board);
		
			for (int i = 0; i < allSimulatedBoards.getCardSetList().size(); i++) {
				
				double highestEnemyScore = 0;
				CardSet actualBoard = allSimulatedBoards.getCardSetList().get(i);
				
				//höchster GegnerScore
				for (int j = 0; j < opponentHands.getCardSetList().size(); j++) {
					
					CardSet actualOpponent = opponentHands.getCardSetList().get(j);
					double tempScore = handRanking.evaluate(actualOpponent.merge(actualBoard));
					
					if(tempScore > highestEnemyScore){
						highestEnemyScore = tempScore;
					}
				}
				
				double myScore = handRanking.evaluate(initHand.merge(actualBoard));
				
				if(myScore < highestEnemyScore){
					amountOfWinSplitLose[2] += 1;
				} else if(myScore == highestEnemyScore)
				{
					amountOfWinSplitLose[1] += 1;
				} else {
					amountOfWinSplitLose[0] += 1;
				}
				
			}
		
			int numberOfGames = allSimulatedBoards.getCardSetList().size();
			
			for (int i = 0; i < probabilities.length; i++) {
				probabilities[i] = amountOfWinSplitLose[i]/numberOfGames;
			}		
			
		return probabilities;
	}
	
	public SetOfCardSets simulateAllBoards(CardSet initHand, SetOfCardSets opponentHands, CardSet board){
		
		SetOfCardSets allSimulatedBoards = new SetOfCardSets();
		CardSet forbiddenCards;
		
		forbiddenCards = initHand.merge(board);

		for (int i = 0; i < opponentHands.getCardSetList().size(); i++) {
			forbiddenCards = forbiddenCards.merge(opponentHands.getCardSetList().get(i));
		}
		
		int numberOfCards = 5 - board.getCardList().size();

		allSimulatedBoards = getCardCombinations(numberOfCards, getAllowedCards(forbiddenCards));

		return allSimulatedBoards;
	}
	
	public ArrayList<SetOfCardSets> simulateAllOpponentHandComb(int numberOfOpponents, CardSet allowedCards){
		
		if(numberOfOpponents > 0){
			numberOfOpponents -= 1;
		} else{
			return null;	//keine Gegner zu simulieren!
		}
		
		ArrayList<SetOfCardSets> allOpponentSetComb = new ArrayList<SetOfCardSets>();
		
		SetOfCardSets opponentHands = this.getOpponentHands(allowedCards);
		
		
		for (int i = 0; i < opponentHands.getCardSetList().size(); i++) {
			SetOfCardSets newOpponentHands = new SetOfCardSets();
			newOpponentHands.addCardSet(opponentHands.getCardSetList().get(i));
			allOpponentSetComb.add(newOpponentHands);
		}
		
		allOpponentSetComb = this.calculateOpponentHandComb(numberOfOpponents, allowedCards, allOpponentSetComb);
		
		return allOpponentSetComb;
	}
	
	//TODO Seiteneffekte beseitigen
	public ArrayList<SetOfCardSets> calculateOpponentHandComb(int numberOfOpponents, CardSet allowedCards, ArrayList<SetOfCardSets> allOpponentSetComb){
				
		ArrayList<SetOfCardSets> newAllOpponentHandComb = new ArrayList<SetOfCardSets>();
		
		if (numberOfOpponents > 0) {
			numberOfOpponents = numberOfOpponents - 1;

			SetOfCardSets nextOpponentsHandConfig = this.getOpponentHands(allowedCards);

			for (int i = 0; i < allOpponentSetComb.size(); i++) {

				SetOfCardSets previousOpponents = allOpponentSetComb.get(i);

				for (int j = 0; j < nextOpponentsHandConfig.getCardSetList().size(); j++) {

					CardSet nextOpponentsHand = nextOpponentsHandConfig.getCardSetList().get(j);

					if (!previousOpponents.containsCardSet(nextOpponentsHand)) {

						SetOfCardSets newOpponentConfig = new SetOfCardSets();

						for (int l = 0; l < previousOpponents.getCardSetList().size(); l++) {

							CardSet previousOpp = previousOpponents.getCardSetList().get(l);
							newOpponentConfig.addCardSet(previousOpp);
						}
						newOpponentConfig.addCardSet(nextOpponentsHand);

						newAllOpponentHandComb.add(newOpponentConfig);
					}
				}
			}

			newAllOpponentHandComb = calculateOpponentHandComb(numberOfOpponents, allowedCards,newAllOpponentHandComb);
		}
		
		
		return newAllOpponentHandComb;
	}
	
	public SetOfCardSets getOpponentHands(CardSet allowedCards){
		
		SetOfCardSets allTwoCardSets = new SetOfCardSets();
		SetOfCardSets allOneCardSets1 = calculateOneCardSets(allowedCards);
		
		for(int i = 0; i < allOneCardSets1.getCardSetList().size(); i++){
			CardSet oneCardSet1 = allOneCardSets1.getCardSetList().get(i);
			
			for(int j = 0; j < allOneCardSets1.getCardSetList().size(); j++){
				CardSet oneCardSet2 = allOneCardSets1.getCardSetList().get(j);
				
				if(oneCardSet2.getCardList().get(0).rateCard() > oneCardSet1.getCardList().get(0).rateCard()){
					CardSet twoCardSet = oneCardSet1.merge(oneCardSet2);
					allTwoCardSets.addCardSet(twoCardSet);
				}
				
			}
		}
		
		return allTwoCardSets;
	}
	
	//numberOfCards muss >= 0 sein
	//Gibt ein neues SetOfCardSets Objekt zur�ck
	public SetOfCardSets getCardCombinations(int numberOfCards, CardSet allowedCards) {
		SetOfCardSets newCardSets = new SetOfCardSets();

		if (numberOfCards >= 1) {

			newCardSets = calculateOneCardSets(allowedCards);
			numberOfCards = numberOfCards - 1;
			newCardSets = this.addCardCombinations(numberOfCards, allowedCards, newCardSets, false);
		}
		
		return newCardSets;
	}
	
	
	//numberOfCards muss >= 0 sein und alle CardSets in actualCombination ist am Anfang nur "new SetOfCardSets()"
	//Gibt ein neues SetOfCardSets Objekt zur�ck
	private SetOfCardSets addCardCombinations(int numberOfCards, CardSet allowedCards, SetOfCardSets actualCombination, boolean done){
		
		SetOfCardSets newCardSets = new SetOfCardSets();
		
		if(numberOfCards > 0){
			
			SetOfCardSets setOfoneCardSets = calculateOneCardSets(allowedCards);
			//System.out.println(actualCombination.getCardSetList().size());
			
			for (int j = 0; j < actualCombination.getCardSetList().size(); j++) {
				
				CardSet solutionSet = actualCombination.getCardSetList().get(j);
				
				for (int i = 0; i < setOfoneCardSets.getCardSetList().size(); i++) {
					CardSet oneCardSet = setOfoneCardSets.getCardSetList().get(i);
					Card oneCard = oneCardSet.getCardList().get(0);
					
					if (!solutionSet.containsHigherRatedCard(oneCard)) {
						CardSet mergedSet = solutionSet.merge(oneCardSet);
						newCardSets.addCardSet(mergedSet);
					}
				}
			}
			numberOfCards = numberOfCards - 1;
			
			newCardSets = this.addCardCombinations(numberOfCards,allowedCards,newCardSets, done);
		}
		
		else if(!done){
			newCardSets = newCardSets.merge(actualCombination);
			done = true;
		}
		
		return newCardSets;	
	}
	
	
	//liefert Kartensets mit jeweils einer Karte
	public SetOfCardSets calculateOneCardSets(CardSet allowedCards){
		SetOfCardSets allOneCardSets = new SetOfCardSets();
		
		for(Card card : allowedCards.getCardList()){
			CardSet newCardSet = new CardSet();
			newCardSet.addCard(card);
			allOneCardSets.addCardSet(newCardSet);
		}
		
		return allOneCardSets;
	}
	
}
